package controller

// posts_controller.go
// 处理与 Post 相关的 handle func

import (
	"errors"
	"gforum/global"
	"gforum/model"
	"gforum/service"
	"gforum/utils"
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
	"net/http"
	"strconv"
)

func Post(ctx *gin.Context) {
	postContent := ctx.MustGet("postContent").(model.PostContent)

	posted, err := service.PostTo(postContent)
	switch {
	case errors.Is(err, service.ErrRecordNotFound):
		utils.NotFound(ctx, err, "User not found")
		return
	case err != nil:
		utils.InternalServerError(ctx, err, "Internal error in update post")
		return
	}

	ctx.JSON(http.StatusOK, posted)
}

// GetPostsByID 返回 post ID 对应的 post
func GetPostsByID(ctx *gin.Context) {
	postID := ctx.Param("id")

	post, err := service.GetPostsByID(postID)
	switch {
	case errors.Is(err, service.ErrRecordNotFound):
		utils.NotFound(ctx, err, "Post not found")
		return
	case err != nil:
		utils.InternalServerError(ctx, err, "Internal error in get post")
		return
	}

	ctx.JSON(http.StatusOK, post)
}

// EditPostByID 编辑对应 ID 的 post
func EditPostByID(ctx *gin.Context) {
	postID := ctx.Param("id")

	var editContent model.PostEdit
	if err := ctx.ShouldBindJSON(&editContent); err != nil {
		utils.BadRequest(ctx, err, "Invalid post content or missing field")
		return
	}

	updatedPost, err := service.UpdatePost(postID, editContent)
	switch {
	case errors.Is(err, service.ErrRecordNotFound):
		utils.NotFound(ctx, err, "Post you want edit not found OR it's already updated")
		return
	case err != nil:
		utils.InternalServerError(ctx, err, "Internal error in update post")
		return
	}

	ctx.JSON(http.StatusOK, updatedPost)
}

// DeletePostByID 删除对应 ID 的 post
func DeletePostByID(ctx *gin.Context) {
	id := ctx.Param("id")

	err := service.DeletePostByID(id)
	switch {
	case errors.Is(err, service.ErrRecordNotFound):
		utils.NotFound(ctx, err, "Post you want to delete not found")
		return
	case err != nil:
		utils.InternalServerError(ctx, err, "Internal error in delete post")
		return
	default:
		utils.StatusOK(ctx, errors.New(""), "Post deleted")
	}
}

// Like 给对应 ID 的 post 点赞
//
// 一个用户只能对一篇帖子点赞一次
func Like(ctx *gin.Context) {
	id := ctx.Param("id")
	postID, _ := strconv.ParseUint(id, 10, 64)

	username := ctx.MustGet("username").(string)

	var user model.User
	if err := global.Db.Where("username = ?", username).First(&user).Error; err != nil {
		utils.NotFound(ctx, err, "Token's user not found")
		return
	}

	var post model.Post
	if err := global.Db.Where("id = ?", postID).First(&post).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			utils.NotFound(ctx, err, "Like: target post not found")
			return
		} else {
			utils.InternalServerError(ctx, err, "Internal error in Like: get post")
			return
		}
	}

	result := global.Db.Where("user_id = ? AND post_id = ?", user.ID, id).First(&model.Like{})
	if errors.Is(result.Error, gorm.ErrRecordNotFound) {
		// 点赞
		like := model.Like{
			UserID: user.ID,
			PostID: uint(postID),
		}
		if err := global.Db.Create(&like).Error; err != nil {
			utils.InternalServerError(ctx, err, "Internal error in like post")
			return
		}

		post.Likes++
	} else if result.Error != nil {
		utils.InternalServerError(ctx, result.Error, "Internal error in like post")
		return
	} else {
		// 取消点赞
		err := global.Db.Where("user_id = ? AND post_id = ?", user.ID, id).Delete(&model.Like{}).Error
		if err != nil { // 已经确认 Like 表中有对应数据,故只存在内部错误
			utils.InternalServerError(ctx, err, "Internal error in like post: delete like relation")
		}
		post.Likes--
	}
	if err := global.Db.Save(&post).Error; err != nil {
		utils.InternalServerError(ctx, err, "Internal error in like post: update post's like count")
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"likes": post.Likes,
	})
}

// GetLikes 返回对应 ID 的 post 的点赞数
func GetLikes(ctx *gin.Context) {
	id := ctx.Param("id")

	var post model.Post
	if err := global.Db.Where("id = ?", id).First(&post).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			utils.NotFound(ctx, err, "Get Likes: target post not found")
			return
		}
		utils.InternalServerError(ctx, err, "Internal error in get likes")
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"likes": post.Likes,
	})
}

// GetAllPosts 按照请求参数返回所有 post
//
// 请求示例：GET /api/posts?topic=R18G&sort=recent&page=1&size=7
//
// 请求参数:
//   - topic: 主题，默认为获取所有主题("all")
//   - sort: 排序方式，默认为时间降序, 可选值为 "recent" 或 "hot"
//   - page: 页码，默认为 1
//   - size: 每页数量，默认为 7
func GetAllPosts(ctx *gin.Context) {
	var query model.AllPostsPagination
	if err := ctx.ShouldBindQuery(&query); err != nil {
		utils.BadRequest(ctx, err, "Invalid query parameters")
		return
	}
	query.Default()
	posts, err := service.GetAllPosts(query)
	switch {
	case errors.Is(err, gorm.ErrRecordNotFound):
		utils.NotFound(ctx, err, "No posts found")
		return
	case err != nil:
		utils.InternalServerError(ctx, err, "Internal error in get all posts")
		return
	}

	total, err := service.GetTotalPostsSizeWithTopic(query.Topic)
	if err != nil {
		utils.InternalServerError(ctx, err, "Internal error in get total size")
		return
	}

	totalPages := service.GetTotalPage(query.Size, total)

	ctx.JSON(http.StatusOK, gin.H{
		"page":       query.Page,
		"size":       len(*posts),
		"total":      total,
		"totalPages": totalPages,
		"posts":      posts,
	})
}

// SearchPosts 搜索 Post
//
// 请求示例：GET /api/search?q=Go&topic=R18G&sort=recent&page=1&size=7
//
// 请求参数:
//   - q: 搜索关键字，必填
//   - topic: 主题，默认为获取所有主题("all")
//   - sort: 排序方式，默认为时间降序, 可选值为 "recent" 或 "relevance"
//   - page: 页码，默认为 1
//   - size: 每页数量，默认为 7
func SearchPosts(ctx *gin.Context) {
	var query model.SearchPagination
	if err := ctx.ShouldBindQuery(&query); err != nil {
		utils.BadRequest(ctx, err, "Invalid query parameters")
		return
	}
	query.Default()

	// 创建两个 channel 分别用于接收搜索结果和结果数量
	postsChan := make(chan struct {
		posts *[]model.Post
		err   error
	})
	totalChan := make(chan struct {
		total int64
		err   error
	})

	// 并行执行搜索查询和结果数量查询
	go func() {
		posts, err := service.GetSearchResult(query)
		postsChan <- struct {
			posts *[]model.Post
			err   error
		}{posts, err}
	}()

	go func() {
		total, err := service.GetSearchResultSize(query)
		totalChan <- struct {
			total int64
			err   error
		}{total, err}
	}()

	// 接收查询结果
	postsResult := <-postsChan
	totalResult := <-totalChan

	// 检查搜索结果错误
	if postsResult.err != nil {
		if errors.Is(postsResult.err, gorm.ErrRecordNotFound) {
			utils.NotFound(ctx, postsResult.err, "No posts found")
			return
		}
		utils.InternalServerError(ctx, postsResult.err, "Internal error in search posts")
		return
	}

	// 检查结果数量查询错误
	if totalResult.err != nil {
		utils.InternalServerError(ctx, totalResult.err, "Internal error in get total size")
		return
	}

	totalPages := service.GetTotalPage(query.Size, totalResult.total)

	ctx.JSON(http.StatusOK, gin.H{
		"page":       query.Page,
		"size":       len(*postsResult.posts),
		"total":      totalResult.total,
		"totalPages": totalPages,
		"posts":      postsResult.posts,
	})
}
